package v1

import (
	"errors"
	"net/http"
	"strconv"
	"zhejianglab/gin-apiserver/model"
	"zhejianglab/gin-apiserver/model/request"

	"github.com/astaxie/beego/validation"
	"github.com/gin-gonic/gin"
)

// AddNewAlgo create new Algorithm
// @Produce  json
// @Tags Algorithm
// @Param AlgorithmInfo body request.AlgorithmInfo true "request.AlgorithmInfo"
// @Success 201 {object} gin.H "{"code":201,"msg":"AddNewAlgorithm successfully"}"
// @Failure 409 {object} gin.H "{"code":500,"msg":"AddNewAlgorithm: algorithm name is exist"}"
// @Failure 500 {object} gin.H "{"code":500,"msg":"AddNewAlgorithm: algorithm name is invalid"}"
// @Router /api/v1/algorithm/new [post]
func AddNewAlgo(c *gin.Context) {
	var err error

	var Algo request.AlgorithmInfo
	err = c.ShouldBindJSON(&Algo)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error(), "Error Message ": err})
		return
	}
	algoInfo := model.AlgorithmInfo{
		Name:          Algo.Name,
		Version:       Algo.Version,
		Type:          Algo.Type,
		AlgorithmPath: Algo.AlgorithmPath,
		ModelPath:     Algo.ModelPath,
		ConfigPath:    Algo.ConfigPath,
		Description:   Algo.Description,
		UserID:        Algo.UserID,
	}

	valid := validation.Validation{}

	// 校验算法名、pathKey的有效性
	valid.Required(Algo.Name, "name").Message("算法名称不能为空")
	if valid.HasErrors() {
		err = errors.New("AddNewAlgorithm: algorithm name is invalid")
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
		return
	}
	valid.Required(Algo.AlgorithmPath, "algorithm_path").Message("algorithm_path不能为空")
	if valid.HasErrors() {
		err = errors.New("AddNewAlgorithm: algorithm_path is invalid")
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
		return
	}
	valid.Range(Algo.Type, 0, 4, "type").Message("算法类型在0-4之间")
	if valid.HasErrors() {
		err = errors.New("AddNewAlgorithm: algorithm type is invalid")
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
		return
	}

	if model.ExistAlgoByName(Algo.Name) {
		err = errors.New("AddNewAlgorithm: algorithm name is exist")
		c.JSON(http.StatusConflict, gin.H{"status": http.StatusConflict, "message": err.Error()})
		return
	}

	data, err := model.AddNewAlgo(&algoInfo)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
		return
	}
	algorithm, ok := data.(*model.AlgorithmInfo)
	if !ok {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": "AddNewAlgorithm failed!"})
		return
	}
	c.JSON(http.StatusCreated, gin.H{"status": http.StatusCreated, "message": "AddNewAlgorithm successfully!", "data": algorithm.ID})
}

// GetAlgo get algorithm info from db
// @Produce  json
// @Tags Algorithm
// @Param id path int true "ID"
// @Success 200 {object} gin.H "{"code":200,"msg":"GetAlgo successfully"}"
// @Failure 500 {object} gin.H "{"code":500,"msg":"GetAlgo failed!"}"
// @Router /api/v1/algorithm/{id} [get]
func GetAlgo(c *gin.Context) {
	id := c.Param("id")
	data, err := model.GetAlgo(id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "GetAlgo successfully!", "data": data})
}

// GetAlgoWithType get algorithm info from db
// @Produce  json
// @Tags Algorithm
// @Param type path int true "type"
// @Param user_id path int true "user_id"
// @Success 200 {object} gin.H "{"code":200,"msg":"GetAlgo successfully"}"
// @Failure 500 {object} gin.H "{"code":500,"msg":"GetAlgo failed!"}"
// @Router /api/v1/algorithm [get]
func GetAlgoWithType(c *gin.Context) {

	typeID := c.Query("type")
	userID := c.Query("user_id")
	var err error

	if typeID == "" && userID == "" {
		data, err := model.GetAllAlgos()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
			return
		}
		c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "GetAlgo successfully!", "data": data})
		return
	}

	data, err := model.GetAlgoWithType(typeID, userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
		return
	}
	c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "GetAlgo successfully!", "data": data})
}

// UpdateAlgo create new algorithm
// @Produce  json
// @Tags Algorithm
// @Param AlgorithmInfo body request.AlgorithmInfo true "request.AlgorithmInfo"
// @Success 201 {object} gin.H "{"code":201,"msg":"UpdateAlgo successfully"}"
// @Failure 409 {object} gin.H "{"code":500,"msg":"UpdateAlgo: algorithm name is exist"}"
// @Failure 500 {object} gin.H "{"code":500,"msg":"UpdateAlgo: algorithm name is invalid"}"
// @Router /api/v1/algorithm/{id} [put]
func UpdateAlgo(c *gin.Context) {
	var err error
	data := make(map[string]interface{})
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
		return
	}

	var algo request.AlgorithmInfo

	err = c.ShouldBindJSON(&algo)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": "UpdateAlgo failed!" + err.Error()})
		return
	}

	if algo.Name != "" {
		if model.ExistAlgoByName(algo.Name) {
			err = errors.New("UpdateAlgo: algorithm name is exist")
			c.JSON(http.StatusConflict, gin.H{"status": http.StatusConflict, "message": err.Error()})
			return
		}
		data["name"] = algo.Name
	}
	if algo.AlgorithmPath != "" {
		data["algorithm_path"] = algo.AlgorithmPath
	}
	if algo.ModelPath != "" {
		data["model_path"] = algo.ModelPath
	}
	if algo.ConfigPath != "" {
		data["config_path"] = algo.ConfigPath
	}
	if algo.Version != "" {
		data["version"] = algo.Version
	}
	if algo.Description != "" {
		data["description"] = algo.Description
	}
	if algo.Type != 0 {
		data["type"] = algo.Type
	}

	if !model.ExistAlgoByID(id) {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": "Algorithm id is not exists failed!"})
		return
	}
	model.UpdateAlgo(id, data)
	c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "UpdateAlgo successfully!", "data": data})
	return

}

// DeleteAlgo delete case info from db
// @Produce  json
// @Tags Algorithm
// @Param id path int true "ID"
// @Success 200 {object} gin.H "{"code":200,"msg":"DeleteAlgo successfully"}"
// @Failure 500 {object} gin.H "{"code":500,"msg":"DeleteAlgo failed!"}"
// @Failure 500 {object} gin.H "{"code":500,"msg":"id is invalid!"}"
// @Router /api/v1/algorithm/{id} [delete]
func DeleteAlgo(c *gin.Context) {
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": err.Error()})
		return
	}

	valid := validation.Validation{}
	valid.Min(id, 1, "id").Message("ID必须大于0")

	if !valid.HasErrors() {
		if model.ExistAlgoByID(id) {
			model.DeleteAlgo(id)
			c.JSON(http.StatusOK, gin.H{"status": http.StatusOK, "message": "DeleteAlgo successfully!"})
		} else {
			c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": "DeleteAlgo failed!"})
			return
		}
	} else {
		c.JSON(http.StatusInternalServerError, gin.H{"status": http.StatusInternalServerError, "message": "id is invalid!"})
		return
	}
}
